#include "client.h"

//为防止一次不能发送完想发送的数据，此处采用Sendn。
int Sendn(int fd, void* buf, int len) {
    char* bufChar = (char*)buf;
    int size = len;
    while(size > 0) {
        int ret = send(fd, bufChar, size, 0);
        if(ret < 0) {
            perror("sendn");
            return len - size;
        }
        size -= ret;
        bufChar += ret;
    }
    return len - size;
}

// 该函数传入套接字对象的文件描述符和文件路径，
// 先判断文件是否存在，若不存在则发送 0，
// 存在则先发送 1，再发送文件名，
// 再发送文件大小，接着发送文件内容，
// 为防止粘包问题，发送的所有数据均采用 train 结构的私有协议，
// 最后若文件不存在则返回字符串 "file not exist""，
// 若发送成功则返回字符串 "transfer succeed"。

char* SendFile(char* pathName, int peerFd) {
    //此处将文件从文件路径中分离出来
    char dirPath[1000] = {0};
    char fileName[1000] = {0};
    for(int i = strlen(pathName); i > 0; i--) {
        if(pathName[i - 1] == '/') {
            memcpy(dirPath, pathName + i - 1, strlen(pathName) - i - 1);
            memcpy(fileName, pathName, i + 1);
        }
    }
    //判断文件是否存在，并设标志位fileExist
    int fileExist;
    DIR* dirp = opendir(dirPath);
    if(dirp == NULL) {
        perror("opendir");
        return "transfer failed";
    }
    struct dirent* entry;
    while(entry = readdir(dirp)) {
        if(!strcmp(entry->d_name, fileName)) {
            fileExist = 1;
            break;
        }
    }
    if(entry == NULL) {
        fileExist = 0;
    }
    //定义train类型的对象，发送该标志位
    Train_t train;
    memset(&train, 0, sizeof(Train_t));
    train.lenth = sizeof(int);
    memcpy(train.buf, &fileExist, train.lenth);
    Sendn(peerFd, &train + 4, train.lenth);
    //根据标志位的值判断是否发送文件
    if(fileExist == 0) {
        return "file not exist\n";
    } else {
        //若文件存在，则打开文件
        int fd = open(pathName, O_RDONLY);
        if(fd == -1) {
            perror("open");
            return "transfer failed";
        }
        //发送文件名
        train.lenth = strlen(fileName);
        memcpy(train.buf, fileName, train.lenth);
        Sendn(peerFd, &train, 4 + train.lenth);
        //发送文件大小
        off_t FileSize;
        memset(&train, 0, sizeof(Train_t));
        struct stat statbuf;
        int ret = fstat(fd, &statbuf);
        if(ret == -1) {
            perror("fstat");
            return "transfer failed\n";
        }
        FileSize = statbuf.st_size;
        train.lenth = sizeof(off_t);
        memcpy(train.buf, &statbuf.st_size, train.lenth);
        Sendn(peerFd, &train + 4, train.lenth);
        //接收对方所告知的已传文件大小
        off_t byteHaveSend = 0;
        ret = recv(peerFd, &byteHaveSend, sizeof(off_t), MSG_WAITALL);
        if(ret == -1) {
            perror("recv");
            return "transfer failed";
        }
        //采用零拷贝发送文件内容
        void* buf = mmap(NULL, FileSize - byteHaveSend, PROT_WRITE | PROT_READ, MAP_SHARED, fd, byteHaveSend);
        if(buf == MAP_FAILED) {
            perror("mmap");
            return "transfer failed";
        }
        Sendn(peerFd, buf, FileSize - byteHaveSend);
        munmap(buf, FileSize);
        //关闭文件并返回
        close(fd);
        return "tansfer succeed\n";
    }
}



